<template>
  <div :class="classes" class="q-charts shadow-1 q-pa-md relative-position">
    <div ref="QChart" class="fit"></div>
    <MEmpty v-if="showEmpty" class="fit absolute-top"></MEmpty>
    <q-inner-loading :showing="loading" class="bg-transparent">
      <q-spinner-ios color="primary" size="3rem" />
    </q-inner-loading>
    <q-resize-observer @resize="onResize" />
  </div>
</template>

<script setup>
import MEmpty from 'components/Global/MEmpty.vue'
import { init } from 'src/boot/echarts'
import { computed, onUnmounted, ref, watchPostEffect } from 'vue'
import { adaptFontSize, getTypeof, isEmpty } from 'src/utils/utils'

const QChart = ref(null)

const props = defineProps({
  rows: Array,
  grid: Object,
  ySite: Array,
  xSite: Object,
  title: Object,
  colors: Array,
  stack: Object,
  charts: Array,
  option: Object,
  columns: Array,
  legend: Object,
  format: Boolean,
  xRotate: Number,
  tooltip: Object,
  toolbox: Object,
  classes: String,
  loading: Boolean,
  chartSize: Object,
  emptyData: Boolean,
  hideTooltips: Array,
  dataZoomNum: Number,
  formatTooltip: Function,
  labelMap: [Object, Array]
})

const emits = defineEmits(['click', 'mouseout', 'mouseover', 'selectchanged', 'update:chartSize'])

const showEmpty = computed(() => {
  const { emptyData, rows } = props
  if (rows) return !rows.length
  return emptyData
})

const dataset = computed(() => {
  const { columns: dimensions, dataset, rows: source = [] } = props
  if (dataset) return dataset
  return { source, dimensions }
})

const xAxis = computed(() => {
  const { xSite = {} } = props
  return {
    type: 'category', name: '', nameLocation: 'start',
    nameTextStyle: {
      align: 'center', verticalAlign: 'top', padding: [5, 0, 0, 5]
    },
    axisTick: { show: false },
    axisLine: { show: false },
    axisLabel: {
      interval: 0, rotate: props.xRotate || 0, fontSize: adaptFontSize(14), overflow: 'breakAll',
      hideOverlap: true, padding: [0, 4]
    }, ...xSite
  }
})

const yAxis = computed(() => {
  const { ySite } = props
  if (isEmpty(ySite)) {
    return {
      splitLine: { lineStyle: { width: 0.5 } },
      axisLabel: { fontSize: adaptFontSize(14) }
    }
  }
  const yAxis = []
  for (const item of ySite) {
    if (getTypeof(item) === 'object') {
      yAxis.push({
        name: item.name, type: 'value', splitLine: { lineStyle: { width: 0.5 } }, nameLocation: 'end', nameGap: 20,
        axisLabel: { fontSize: adaptFontSize(14), formatter: '{value}' + (item.format || '') },
        alignTicks: true, axisTick: { show: false }, axisLine: { show: false }, ...item
      })
    } else {
      yAxis.push({
        type: 'value', alignTicks: true,
        axisLabel: { formatter: '{value}' + (item || ''), fontSize: adaptFontSize(14) },
        splitLine: { lineStyle: { width: 0.5 } }, axisTick: { show: false }, axisLine: { show: false }
      })
    }
  }
  return yAxis
})

const SERIESATTRS = computed(() => {
  const size10 = adaptFontSize(10), size8 = size10 * 0.8
  if (!props.format) return { barMaxWidth: size10 * 1.5, barMinWidth: size8, symbolSize: size8, smooth: true }
  return {
    barMaxWidth: size10 * 1.5, barMinWidth: size8, symbolSize: size8, smooth: true,
    itemStyle: { borderRadius: [size10 * 0.2, size10 * 0.2, 0, 0] }
  }
})

const getStack = field => {
  if (isEmpty(props.stack)) return
  for (const key in props.stack) {
    const list = props.stack[key] || []
    if (list.includes(field)) return key
  }
}

const series = computed(() => {
  const { charts, columns } = props
  if (!charts) {
    if (isEmpty(columns)) return []
    const series = []
    for (const item of columns) {
      if (item !== columns[0]) series.push({ type: 'bar', name: item, ...SERIESATTRS.value, stack: getStack(item) })
    }
    return series
  }
  const series = []
  for (const item of charts) {
    if (getTypeof(item) === 'object') {
      const seriesItem = { ...SERIESATTRS.value, ...item }
      if (item.color) {
        seriesItem.itemStyle = seriesItem.itemStyle || {}
        seriesItem.itemStyle.color = { x: 0, y: 0, x2: 0, y2: 1, type: 'linear', colorStops: item.color }
      }
      if (item.emColor) {
        seriesItem.emphasis = seriesItem.emphasis || {}
        seriesItem.emphasis.itemStyle = seriesItem.emphasis.itemStyle || {}
        seriesItem.emphasis.itemStyle.color = { x: 0, y: 0, x2: 0, y2: 1, type: 'linear', colorStops: item.emColor }
      }
      series.push(seriesItem)
    } else series.push({ type: item, ...SERIESATTRS.value })
  }
  return series
})

const getLabel = value => {
  if (isEmpty(props.labelMap)) return value
  if (getTypeof(props.labelMap) === 'array') return value
  return props.labelMap[value] || value
}

const getValue = (list, field) => {
  if (isEmpty(list)) return '-'
  if (props.formatTooltip) return props.formatTooltip(list, field)
  if (getTypeof(list) === 'object') return isEmpty(list[field]) ? '-' : list[field]
  return list
}

const formatLabel = (list = []) => {
  let str = '', name = ''
  const { hideTooltips = [] } = props
  if (getTypeof(list) === 'object') {
    if (hideTooltips.includes(list.seriesName)) return ''
    return `${list.name}<br />${list.marker} ${list.name}：${list.value}`
  }
  for (const obj of list) {
    if (hideTooltips.includes(obj.seriesName)) continue
    if (!name) name = obj.axisValue || obj.name
    str += `${obj.marker} ${getLabel(obj.seriesName)}：${getValue(obj.value, obj.seriesName)}<br />`
  }
  return `${name}<br />${str}`
}

const option = computed(() => {
  if (props.option) return props.option
  const { grid = {}, legend = {}, title = {}, tooltip = {} } = props, size = adaptFontSize(10)
  const option = {
    dataset: dataset.value,
    legend: {
      top: size, show: !isEmpty(props.labelMap), data: null, itemGap: size, textStyle: { fontSize: size },
      formatter: value => getLabel(value), ...legend
    },
    grid: { top: size * 3, left: size * 5, right: size * 3, bottom: size * 3, ...grid },
    title: {
      top: -size * 0.5, text: '', textStyle: {
        fontSize: adaptFontSize(16), color: '#333', fontFamily: 'PingFang SC', fontWeight: '600'
      }, ...title
    },
    toolbox: props.toolbox,
    tooltip: {
      trigger: 'axis', className: 'echarts-tooltip-cls', backgroundColor: 'rgba(255,255,255,0.95)', borderWidth: 0,
      padding: [size * 1, size * 1.5], // axisPointer: { type: 'none' },
      textStyle: { color: '#757575', fontSize: size * 1.4, fontWeight: 'bold' },
      formatter: list => formatLabel(list), ...tooltip
    },
    xAxis: xAxis.value, yAxis: yAxis.value, series: series.value
  }
  const LENGTH = (props.rows || []).length, NUM = props.dataZoomNum || 30
  if (LENGTH > NUM) {
    option.dataZoom = [{
      show: true, type: 'slider', xAxisIndex: 0, borderColor: 'transparent',
      end: Math.floor(NUM * 100 / LENGTH), maxSpan: Math.floor(NUM * 100 / LENGTH),
      showDetail: false, brushSelect: false
    }]
  } else option.dataZoom = [{ show: false }]
  if (props.colors) option.color = props.colors
  return option
})

let qxEcharts = null
const initData = () => {
  if (!QChart.value) return
  if (!qxEcharts) {
    qxEcharts = init(QChart.value)
    qxEcharts.on('click', e => emits('click', e)) // 报表添加点击事件
    qxEcharts.on('mouseout', e => emits('mouseout', e)) // 报表添加鼠标移出事件
    qxEcharts.on('mouseover', e => emits('mouseover', e)) // 报表添加鼠标移入事件
    qxEcharts.on('selectchanged', e => emits('selectchanged', e)) // 报表添加选择change事件
  }
  qxEcharts.setOption(option.value)
}
watchPostEffect(initData)
const onResize = size => {
  emits('update:chartSize', size)
  if (qxEcharts) return qxEcharts.resize() // 非首次页面size变化，调整图表size
  if (QChart.value) initData() // 首次加载时，初始化图表
}
onUnmounted(() => qxEcharts && qxEcharts.dispose())

const getChartRef = () => qxEcharts
defineExpose({ getChartRef })
</script>